package interfaces

import (
	"deploy/interfaces/cmdb"
	"deploy/interfaces/db"
	"deploy/schema"
	"deploy/utils"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"sync"
	"time"
)

var upgrader = websocket.Upgrader{
	ReadBufferSize:  10240,
	WriteBufferSize: 10240,
	// 允许所有的CORS 跨域请求，正式环境可以关闭
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

type Server struct {
	// 监控地址
	ServerAddr string
	// 在线用户列表
	OnlineClientMap map[string]*Client
	// 用户map的锁
	mapLock sync.RWMutex
	// 广播chan
	BroadcastChan chan []byte
	// ServerConf
	Conf *utils.ServerConf
	// Db
	Db *db.MysqlStruct
}

// 创建一个实例化服务对象
func NewServer(conf *utils.ServerConf, db *db.MysqlStruct) *Server {
	return &Server{ServerAddr: conf.Listen,
		OnlineClientMap: make(map[string]*Client),
		BroadcastChan:   make(chan []byte),
		Conf:            conf,
		Db:              db,
	}
}

// 启动服务
func (s *Server) Start() {
	go s.Broadcast()
	router := gin.Default()
	router.Use(Cors())
	// router.Use(IpWall)
	//gin.SetMode(gin.ReleaseMode)
	router.GET("/ws2", s.wsHandler)
	s.SetupRouter(router)
	//httpHandle2.SetupRouter(s1,router)
	//router.POST("/deploy/runtest", JobEach)
	router.Run(s.ServerAddr)
	//httpHandle.HandleFunc("/ws",s.wsHandler)
	// 等待中断信号以优雅地关闭服务器（设置 5 秒的超时时间）
	quit := make(chan os.Signal)
	signal.Notify(quit, os.Interrupt)
	<-quit
}

func (s *Server) wsHandler(c *gin.Context) {
	wsSocket, err := upgrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		log.Println("建立websocket 失败", err.Error())
		return
	}
	id := strings.Split(c.Request.RemoteAddr, ":")[1]
	// 创建client
	intId, err := strconv.Atoi(id)
	if err != nil {
		log.Println(err)
	}
	client := NewClient(wsSocket, intId, fmt.Sprintf("游客%d", intId), s)
	// 把client加入到在线列表中
	s.mapLock.Lock()
	s.OnlineClientMap[client.Name] = client
	s.mapLock.Unlock()
	log.Println("--------")
	log.Println("在线client数为：", len(s.OnlineClientMap))

	// 启动这个用户的 收消息goroutine和发消息goroutine
	go client.Send()
	go client.Receive()
}

// UpdateClient 更新agent的信息
func (c *Client) UpdateClient(m utils.ClientInfoMessage) {
	// 构建返回数据
	str := utils.StrMessage{}
	defer func() {
		if reerr := recover(); reerr != nil {
			log.Println("捕获异常:", reerr)
			str.Str = fmt.Sprintf("%v", reerr)
		}
		msg := utils.NewMessage("12345678", 5, utils.ToJson(&str))
		c.server.SendTo(c, msg)
	}()
	ipAddr := c.ws.RemoteAddr().String()
	ip := strings.Split(ipAddr, ":")[0]
	log.Println(ip)
	// 先删除旧的连接信息
	oldname := c.Name
	c.server.mapLock.Lock()
	delete(c.server.OnlineClientMap, c.Name)
	// 添加新的
	c.Name = m.HostName
	c.server.OnlineClientMap[m.HostName] = c
	c.server.mapLock.Unlock()
	//  查找是否数据库有这个
	obj := db.Instance{}
	err := c.server.Db.GetInstanceByHostname(m.HostName, &obj)
	// TODO 根据agent上报的配置类型进行自动分组
	if err != nil {
		if err.Error() == "record not found" {
			//  没有的话写入数据库，
			log.Println("新增一台未记录的服务器：", m.HostName)
			newobj := db.Instance{IsOnline: true, Ip: ip, HostName: m.HostName, AgentVersion: m.AgentVersion,
				HardwareAddr: m.HardwareAddr, System: m.System,
				SystemVersion: m.SystemVersion, SystemPlatform: m.SystemPlatform, Kernel: m.Kernel}
			log.Println(newobj)
			err = c.server.Db.NewInstance(&newobj)
			if err != nil {
				s := fmt.Sprintf("新建实例%s失败:%s", obj.HostName, err.Error())
				str.Str = s
				log.Println(s)
				return
			}
			s := fmt.Sprintf("新建实例%s成功", obj.HostName)
			str.Str = s
			log.Println(s)
			//  并且加入default分组，回复成功
			//err = c.server.Db.UpdateGroupByNameAddAInstance("default_group",&[]db.Instance{newobj})
			//if err!=nil{
			//	s= fmt.Sprintf("实例%s加入default_group 异常:%s",obj.HostName,err.Error())
			//	log.Println(s)
			//	str.Str = str.Str+ s
			//	return
			//}
			//s = fmt.Sprintf("实例%s加入default_group 成功",obj.HostName)
			//str.Str = str.Str+ s
			//log.Println(s)
			return
		}
		s := fmt.Sprintf("查找hardwareaddr:%s数据库记录失败：%s", m.HardwareAddr, err.Error())
		str.Str = s
		log.Println(s)
		return
	}
	if obj.ID != 0 {
		err = c.server.Db.UpdateInstanceById(obj.ID, map[string]interface{}{"is_online": true,
			"host_name":       m.HostName,
			"agent_version":   m.AgentVersion,
			"system":          m.System,
			"system_version":  m.SystemVersion,
			"system_platform": m.SystemPlatform,
			"kernel":          m.Kernel,
			"ip":              ip,
		})
		if err != nil {
			s := fmt.Sprintf("更新实例 %s Haddr %s 状态为在线时失败:%s", obj.HostName, obj.HardwareAddr, err.Error())
			str.Str = s
			log.Println(s)
			return
		}
		s := fmt.Sprintf("更新实例%s状态为在线成功", obj.HostName)
		str.Str = s
		log.Println(s)
		return
	}
	log.Printf("client: %s修改name为%s\n", oldname, c.Name)
}

// Domsg 解析数据，处理发送消息的业务
func (s *Server) Domsg(c *Client, m utils.Message) {
	switch m.MsgType {
	case 1: // pingpong
		c.Pong()
	case 3: // 任务执行结果
		j := utils.JobResult{}
		err := json.Unmarshal(m.Msg, &j)
		if err != nil {
			log.Println("解析数据出错：", err.Error())
			return
		}
		c.JobResult(j)
	case 4: // 更新主机名的请求
		//log.Println("work")
		j := utils.ClientInfoMessage{}
		err := json.Unmarshal(m.Msg, &j)
		if err != nil {
			log.Println("解析数据出错：", err.Error())
			return
		}
		c.UpdateClient(j)
	case 6: // 同步的来处理agent回复的listdir结果
		j := utils.FileListMessage{}
		err := json.Unmarshal(m.Msg, &j)
		if err != nil {
			log.Println("解析数据出错：", err.Error())
		}
		c.SyncChan <- j
		// 文件名 文件大小 文件的md5 主机名 处理任务的id 写入数据库中
	case 7: // 同步处理agent回复的日志结果
		j := utils.UploadFileInfoMessage{}
		err := json.Unmarshal(m.Msg, &j)
		if err != nil {
			log.Println("解析数据出错：", err.Error())
		}
		c.SyncChan <- j
	case 10:
		j := schema.AgentConf{}
		err := json.Unmarshal(m.Msg, &j)
		if err != nil {
			log.Println("解析数据出错：", err.Error())
		}
		c.SyncChan <- j
	case 12: //
		j := schema.SchemaControlAvoms{}
		err := json.Unmarshal(m.Msg, &j)
		if err != nil {
			log.Println("解析数据出错：", err.Error())
		}
		c.SyncChan <- j
	default:
		log.Println("未知的消息类型：", m.MsgType)
	}
}

// 回复心跳消息
func (c *Client) Pong() {
	msg := utils.NewMessage("12345678", 1, []byte(`{"key":"pong"}`))
	c.outChan <- msg
}

// 处理client上报回来的任务数据
func (c *Client) JobResult(j utils.JobResult) {

	//obj := db.JobInstance{
	//	JobId: j.JobId,
	//	Status: j.JobStatus,
	//	JobLog:j.JobLog,
	//	Instance: c.Name,
	//}

	if j.JobStatus == utils.Running.Str() {
		StartTime := time.Now()
		update := map[string]interface{}{
			"status":     j.JobStatus,
			"job_log":    j.JobLog,
			"start_time": StartTime,
		}
		c.server.Db.UpdateJobInstancesByJobIdANdInstance(j.JobId, c.Name, update)
	} else if j.JobStatus == utils.Sucess.Str() {
		update := map[string]interface{}{
			"status":   j.JobStatus,
			"job_log":  j.JobLog,
			"end_time": time.Now(),
		}
		// 更新本表的状态
		err := c.server.Db.UpdateJobInstancesByJobIdANdInstance(j.JobId, c.Name, update)
		if err != nil {
			log.Printf("更新实例%s的任务状态为%s时异常:%v\n", c.Name, j.JobStatus, err.Error())
			return
		}
		// 更新任务总状态中的数据
		objStatus := db.JobStatus{}
		c.server.Db.GetJobStatusByJobId(j.JobId, &objStatus)
		//err = c.server.Db.GetJobStatusByJobId(j.JobId,&objStatus)
		//if err != nil{
		//	log.Printf("查找%d记录异常：%s",j.JobId,err.Error())
		//	return
		//}
		count := objStatus.OkCount
		data := map[string]interface{}{"ok_count": count + 1}
		if objStatus.InstanceCount == count+1 {
			data["status"] = utils.Sucess.Str()
		}
		c.server.Db.UpdateJobStatusByJobId(j.JobId, data)
		log.Printf("更新任务%d的完成数为%d\n", j.JobId, count+1)
	} else if j.JobStatus == utils.Stoped.Str() || j.JobStatus == utils.Fail.Str() {
		update := map[string]interface{}{
			"status":   j.JobStatus,
			"job_log":  j.JobLog,
			"end_time": time.Now(),
		}
		c.server.Db.UpdateJobInstancesByJobIdANdInstance(j.JobId, c.Name, update)
		// 更新总任务状态为异常
		c.server.Db.UpdateJobStatusByJobId(j.JobId, map[string]interface{}{"status": j.JobStatus})
	}

	msg := utils.NewMessage("12345678", 1, []byte(`{"key":"ping"}`))
	c.outChan <- msg
}

// 向每个用户的chan发送数据
func (s *Server) Broadcast() {
	for {
		buff := <-s.BroadcastChan
		s.mapLock.Lock()
		// 向每一个用户的发送chan中发送数据
		for _, client := range s.OnlineClientMap {
			// fmt.Println(client.Name,string(buff))
			client.outChan <- buff
		}
		s.mapLock.Unlock()
	}
}

// 向指定的多个用户发送消息
func (s *Server) MulitUser(users []*Client, msg []byte) {
	//buff  := <- s.BroadcastChan
	s.mapLock.Lock()
	// 向每一个用户的发送chan中发送数据
	for _, client := range users {
		log.Printf("向agent%s发送消息：", client.Name)
		client.outChan <- msg
	}
	s.mapLock.Unlock()
}

// 向指定的一个用户发送消息
func (s *Server) SendTo(c *Client, msg []byte) {
	log.Printf("向agent%s发送消息：", c.Name)
	c.outChan <- msg
}

// 查找出指定的一个client
func (s *Server) FindAClientByNameFromServer(name string) *Client {
	s.mapLock.Lock()
	defer s.mapLock.Unlock()
	for _, c := range s.OnlineClientMap {
		if name == c.Name {
			return c
		}
	}
	return nil
}

// 通过主机名列表，返回已注册的client,和不在server中注册的主机名
func (s *Server) FindClientByNameFromServer(names []string) ([]*Client, []string) {
	rc := []*Client{}
	rs := []string{}
	s.mapLock.Lock()
	for _, name := range names {
		var has = false
		for _, c := range s.OnlineClientMap {
			log.Println("参数主机名是:", name, "server中的agent主机名是", c.Name)
			if name == c.Name {
				rc = append(rc, c)
				has = true
				break
			}
		}
		if has == false {
			rs = append(rs, name)
		}
	}
	s.mapLock.Unlock()
	return rc, rs
}

// 从cmdb中查找出一个client 返回他的tag

func (s *Server) FindAClientByTagFromCmdb(tag string) []string {
	result := []string{}
	defer func() {
		if err := recover(); err != nil {
			log.Println("捕获异常:", err)
			result = nil
		}
	}()
	page := 100
	p := 1
	for {
		req := utils.NewRequestStruct(s.Conf.ThirdApi.N9e, fmt.Sprintf(
			"/api/ams-ce/tags/hostTagGets?limit=%d&p=%d&tags=%s", page, p, tag))
		req.Header.Add("X-User-Token", s.Conf.ThirdApi.Token)
		body, err := req.Get()
		if err != nil {
		}
		//log.Println(string(body))
		var hosts cmdb.TagSearchData
		err = json.Unmarshal(body, &hosts)
		if err != nil {
			panic(fmt.Sprintf("解析返回数据错误：%s", err.Error()))
		}
		for _, host := range hosts.Dat.List {
			result = append(result, host.Name)
		}
		if hosts.Dat.Total > page*p {
			p = p + 1
		} else {
			return result
		}
	}
}
